#include "StdAfx.h"
#include "ManagedImageConverter.h"
//////////////////////////////////////////////////////////////////////////
using namespace System::Collections::Generic;
using namespace System::Windows;
using namespace System::Windows::Media;
//////////////////////////////////////////////////////////////////////////
NAMESPACE_BEGIN_UI
//////////////////////////////////////////////////////////////////////////
BitmapPalette^ ManagedImageConverter::CreateGray8DepthPalette()
{
	List<Color>^ colors = gcnew List<Color>(256);
	for (int i = 0; i < 256; i++)
	{
		colors->Add(Color::FromRgb(i, i, i));
	}
	BitmapPalette^ palette = gcnew BitmapPalette(colors);
	return palette;
}

BitmapPalette^ ManagedImageConverter::CreateGray16DepthPalette()
{
	List<Color>^ colors = gcnew List<Color>(65536);
	for (int i = 0; i < 65536; i++)
	{
		colors->Add(Color::FromRgb(i / 0xFF, i / 0xFF, i / 0xFF));
	}
	BitmapPalette^ palette = gcnew BitmapPalette(colors);
	return palette;
}

array<unsigned long>^ ManagedImageConverter::ConvertBitmapPaletteToArray(BitmapPalette^ palette)
{
	IList<Color>^ colors = palette->Colors;
	array<unsigned long>^ palette2 = gcnew array<unsigned long>(colors->Count);
	for (int i = 0; i < palette2->Length; i++)
	{
		palette2[i] = ((unsigned long)((((colors[i].R << 0x10) | (colors[i].G << 8)) | colors[i].B) | (colors[i].A << 0x18)));
	}
	return palette2;
}

void ManagedImageConverter::FillBitmapData8U1C(WriteableBitmap^ data, void* pLine0, int stride, BitmapPalette^ palette)
{
	int width = data->PixelWidth;
	int height = data->PixelHeight;
	int sourceStride = stride;
	int targetStride = data->BackBufferStride;
	unsigned char* pSource = (unsigned char*)pLine0;
	unsigned long* pTarget = (unsigned long*)data->BackBuffer.ToPointer();
	array<unsigned long>^ colors = ConvertBitmapPaletteToArray(palette);
	for (int row = 0; row < height; row++)
	{
		unsigned char* pSourceLine = (unsigned char*)((unsigned char*)pSource + row * sourceStride);
		unsigned long* pTargetLine = (unsigned long*)((unsigned char*)pTarget + row * targetStride);
		for (int col = 0; col < width; col++)
		{
			pTargetLine[col] = colors[pSourceLine[col]];
		}
	}
}

void ManagedImageConverter::FillBitmapData16U1C(WriteableBitmap^ data, void* pLine0, int stride, BitmapPalette^ palette)
{
	int width = data->PixelWidth;
	int height = data->PixelHeight;
	int sourceStride = stride;
	int targetStride = data->BackBufferStride;
	unsigned short* pSource = (unsigned short*)pLine0;
	unsigned long* pTarget = (unsigned long*)data->BackBuffer.ToPointer();
	array<unsigned long>^ colors = ConvertBitmapPaletteToArray(palette);
	for (int row = 0; row < height; row++)
	{
		unsigned short* pSourceLine = (unsigned short*)((unsigned char*)pSource + row * sourceStride);
		unsigned long* pTargetLine = (unsigned long*)((unsigned char*)pTarget + row * targetStride);
		for (int col = 0; col < width; col++)
		{
			pTargetLine[col] = colors[pSourceLine[col]];
		}
	}
}

void ManagedImageConverter::FillBitmapData8U3C(WriteableBitmap^ data, void* pLine0, int stride)
{
	int width = data->PixelWidth;
	int height = data->PixelHeight;
	int sourceStride = stride;
	int targetStride = data->BackBufferStride;
	unsigned char* pSource = (unsigned char*)pLine0;
	unsigned long* pTarget = (unsigned long*)data->BackBuffer.ToPointer();
	for (int row = 0; row < height; row++)
	{
		unsigned char* pSourceLine = (unsigned char*)((unsigned char*)pSource + row * sourceStride);
		unsigned char* pTargetLine = (unsigned char*)((unsigned char*)pTarget + row * targetStride);
		for (int col = 0; col < width; col++)
		{
			pTargetLine[4 * col + 0] = pSourceLine[3 * col + 0];
			pTargetLine[4 * col + 1] = pSourceLine[3 * col + 1];
			pTargetLine[4 * col + 2] = pSourceLine[3 * col + 2];
			pTargetLine[4 * col + 3] = 0xFF;
		}
	}
}

void ManagedImageConverter::FillBitmapData8U4C(WriteableBitmap^ data, void* pLine0, int stride)
{
	int width = data->PixelWidth;
	int height = data->PixelHeight;
	int sourceStride = stride;
	int targetStride = data->BackBufferStride;
	unsigned long* pSource = (unsigned long*)pLine0;
	unsigned long* pTarget = (unsigned long*)data->BackBuffer.ToPointer();
	for (int row = 0; row < height; row++)
	{
		unsigned long* pSourceLine = (unsigned long*)((unsigned char*)pSource + row * sourceStride);
		unsigned long* pTargetLine = (unsigned long*)((unsigned char*)pTarget + row * targetStride);
		for (int col = 0; col < width; col++)
		{
			pTargetLine[col] = pSourceLine[col];
		}
	}
}

WriteableBitmap^ ManagedImageConverter::BuildBitmapFromCache(ManagedImage^ img, WriteableBitmap^ cache)
{
	WriteableBitmap^ bitmap = cache;
	if (bitmap == nullptr || bitmap->Format != PixelFormats::Bgra32 || 
		bitmap->PixelWidth != img->Width || bitmap->PixelHeight != img->Height)
	{
		bitmap = gcnew WriteableBitmap(img->Width, img->Height, 96, 96, PixelFormats::Bgra32, nullptr);
	}
	return bitmap;
}

WriteableBitmap^ ManagedImageConverter::BuildBitmapFromCache(ManagedMat^ img, WriteableBitmap^ cache)
{
	WriteableBitmap^ bitmap = cache;
	if (bitmap == nullptr || bitmap->Format != PixelFormats::Bgra32 || 
		bitmap->PixelWidth != img->Width || bitmap->PixelHeight != img->Height)
	{
		bitmap = gcnew WriteableBitmap(img->Width, img->Height, 96, 96, PixelFormats::Bgra32, nullptr);
	}
	return bitmap;
}

bool ManagedImageConverter::CheckPixelFormatIsSupport(ManagedImage^ img)
{
	if ((img->Channels == 1 && (img->Depth == PixelDepth::Depth8U || img->Depth == PixelDepth::Depth16U)) ||
		((img->Channels == 3 || img->Channels == 4) && img->Depth == PixelDepth::Depth8U))
	{
		return true;
	}
	return false;
}

bool ManagedImageConverter::CheckPixelFormatIsSupport(ManagedMat^ img)
{
	if (img->Type == MatFlag::CV8UC1 || img->Type == MatFlag::CV8UC3 || img->Type == MatFlag::CV8UC4 || img->Type == MatFlag::CV16UC1)
	{
		return true;
	}
	return false;
}

WriteableBitmap^ ManagedImageConverter::Convert(ManagedArr^ img, WriteableBitmap^ cache, BitmapPalette^ palette)
{
	if (img == nullptr)
	{
		return nullptr;
	}
	if (img->GetType() == ManagedImage::typeid || img->GetType()->IsSubclassOf(ManagedImage::typeid))
	{
		ManagedImage^ image = safe_cast<ManagedImage^>(img);
		if (!CheckPixelFormatIsSupport(image))
		{
			throw gcnew System::NotSupportedException("Not support pixel format");
		}

		if (image->Depth == PixelDepth::Depth8U && image->Channels == 1 && (palette == nullptr || palette->Colors->Count != 0xFF))
		{
			palette = CreateGray8DepthPalette();
		}
		else if(image->Depth == PixelDepth::Depth16U && image->Channels == 1 && (palette == nullptr || palette->Colors->Count != 0xFFFF))
		{
			palette = CreateGray16DepthPalette();
		}

		WriteableBitmap^ bitmap = BuildBitmapFromCache(image, cache);
		bitmap->Lock();

		unsigned char* pSource = (unsigned char*)image->Scan0.ToPointer();
		int sourceStride = image->Stride;
		if (image->Origin == ImageOrigin::BottomLeft)
		{
			pSource = pSource + (image->Height - 1) * sourceStride;
			sourceStride = -sourceStride;
		}

		if (image->Channels == 1 && image->Depth == PixelDepth::Depth8U)
		{
			FillBitmapData8U1C(bitmap, pSource, sourceStride, palette);
		}
		else if (image->Channels == 1 && image->Depth == PixelDepth::Depth16U)
		{
			FillBitmapData16U1C(bitmap, pSource, sourceStride, palette);
		}
		else if (image->Channels == 3 && image->Depth == PixelDepth::Depth8U)
		{
			FillBitmapData8U3C(bitmap, pSource, sourceStride);
		}
		else if (image->Channels == 4 && image->Depth == PixelDepth::Depth8U)
		{
			FillBitmapData8U4C(bitmap, pSource, sourceStride);
		}

		bitmap->AddDirtyRect(Int32Rect(0, 0, bitmap->PixelWidth, bitmap->PixelHeight));
		bitmap->Unlock();
		return bitmap;
	}
	else if (img->GetType() == ManagedMat::typeid || img->GetType()->IsSubclassOf(ManagedMat::typeid))
	{
		ManagedMat^ mat = safe_cast<ManagedMat^>(img);
		if (!CheckPixelFormatIsSupport(mat))
		{
			throw gcnew System::NotSupportedException("Not support pixel format");
		}
		if (mat->Type == MatFlag::CV8UC1 && (palette == nullptr || palette->Colors->Count != 0xFF))
		{
			palette = CreateGray8DepthPalette();
		}
		else if (mat->Type == MatFlag::CV16UC1 && (palette == nullptr || palette->Colors->Count != 0xFFFF))
		{
			palette = CreateGray16DepthPalette();
		}

		WriteableBitmap^ bitmap = BuildBitmapFromCache(mat, cache);
		bitmap->Lock();

		unsigned char* pSource = (unsigned char*)mat->Scan0.ToPointer();
		int sourceStride = mat->Stride;
		if (mat->Type == MatFlag::CV8UC1)
		{
			FillBitmapData8U1C(bitmap, pSource, sourceStride, palette);
		}
		else if (mat->Type == MatFlag::CV16UC1)
		{
			FillBitmapData16U1C(bitmap, pSource, sourceStride, palette);
		}
		else if (mat->Type == MatFlag::CV8UC3)
		{
			FillBitmapData8U3C(bitmap, pSource, sourceStride);
		}
		else if (mat->Type == MatFlag::CV8UC4)
		{
			FillBitmapData8U4C(bitmap, pSource, sourceStride);
		}

		bitmap->AddDirtyRect(Int32Rect(0, 0, bitmap->PixelWidth, bitmap->PixelHeight));
		bitmap->Unlock();
		return bitmap;
	}
	else
	{
		throw gcnew System::NotSupportedException();
	}
}

ManagedImage^ ManagedImageConverter::Convert(WriteableBitmap^ bitmap, ManagedImage^ parameter)
{
	throw gcnew System::NotImplementedException();
}

System::Object^ ManagedImageConverter::Convert(System::Object^ value, System::Type^ targetType, System::Object^ parameter, CultureInfo^ culture)
{
	if (value->GetType()->IsSubclassOf(ManagedArr::typeid) && targetType == WriteableBitmap::typeid)
	{
		ManagedArr^ img = safe_cast<ManagedArr^>(value);
		WriteableBitmap^ cache = nullptr;
		BitmapPalette^ palette = nullptr;
		return Convert(img, cache, palette);
	}
	return nullptr;
}

System::Object^ ManagedImageConverter::ConvertBack(System::Object^ value, System::Type^ targetType, System::Object^ parameter, CultureInfo^ culture)
{
	throw gcnew System::NotImplementedException();
}

//////////////////////////////////////////////////////////////////////////
NAMESPACE_END_UI
//////////////////////////////////////////////////////////////////////////